#! /usr/bin/env python

## Another thing that I need to do here is add some docstrings, to build up some documentation. It'll be much easier to do that now, instead of
## putting it off till later and forgetting what I was thinking at the moment in time when I wrote a particular bit of code.

import sys, os.path
from PyQt4 import QtCore, QtGui

class NegaFinSub(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        
        self.defaultDir = os.path.expanduser('~/')
        #self.defaultFileTypes = 'Text: *.txt;;All Files: *.*;;(X)HTML: *.htm *.html;;Python: *.py'     # Works, but not going to use it yet
        
        self.setWindowTitle('NegaFin PyQt4')
        #self.setWindowIcon(QtGui.QIcon('icon.png'))      ## Need to create an icon...
        self.statusBar().showMessage('Ready..')       ## self.tr() (discovered in other code) translates the text
        self.resize(700, 475)

        self.createActions()
        self.createMenu()
        self.createToolBar()
        self.createTextEditor()
        
        self.setConnections()
        
    def createActions(self):
        '''Create actions to fill the menus with.'''
        self.newFileAction = QtGui.QAction('&New File', self)
        self.newFileAction.setShortcut('Ctrl+N')
        self.newFileAction.setStatusTip('New File - Be sure to save previous work!')
        
        self.openAction = QtGui.QAction('&Open', self)
        self.openAction.setShortcut('Ctrl+O')
        self.openAction.setStatusTip('Open File')
        
        self.saveAction = QtGui.QAction('&Save', self)
        self.saveAction.setShortcut('Ctrl+S')
        self.saveAction.setStatusTip('Save File')
        
        self.saveAsAction = QtGui.QAction('Save &As', self)
        self.saveAsAction.setStatusTip('Save File As')
        
        self.exitAction = QtGui.QAction('E&xit', self)  ## In reference to emitting signals, QActions are triggered, not clicked.
        self.exitAction.setShortcut('Ctrl+Q')
        self.exitAction.setStatusTip('Exit Application')
        
        ## The Undo/Redo actions here are already implemented within the QTextEdit widget itself. Just right click in that area, or use the
        ## shortcuts, and Undo/Redo works. However, it doesn't work its best, so I'm going to have to find a way to reimplement the both of
        ## them so they work better.
        self.undoAction = QtGui.QAction('&Undo', self)
        self.undoAction.setShortcut('Ctrl+Z')
        self.undoAction.setStatusTip('Undo Action')
        
        self.redoAction = QtGui.QAction('Re&do', self)
        self.redoAction.setShortcut('Ctrl+Shift+Z')
        self.redoAction.setStatusTip('Redo Action')
        
        self.fontAction = QtGui.QAction('&Font...', self)
        #self.fontAction.setShortcut()
        self.fontAction.setStatusTip('Set Current Font')
        
        self.configureAction = QtGui.QAction('&Configure', self)
        self.configureAction.setStatusTip('Configure Negative Infinity')
        
        self.aboutAction = QtGui.QAction('&About', self)
        self.aboutAction.setStatusTip('About NegaFin PyQt4')
        self.aboutQtAction = QtGui.QAction('About &Qt', self)
        self.aboutQtAction.setStatusTip('About Qt')
        
    ## Here and with the toolbar(s) (plus other areas with repitition like this), it may be useful to have these things created in a loop. Cut
    ## down on the size of the code, and make it more maintainable. Like say, for action in menuaction: menu.addaction(action) or something like
    ## that. Doing that would save having to write a new .addAction line each time a new action is created.
    def createMenu(self):
        self.mBar = self.menuBar()
        self.menuChoiceFile = self.mBar.addMenu('&File')
        self.menuChoiceFile.addAction(self.newFileAction)
        self.menuChoiceFile.addAction(self.openAction)
        self.menuChoiceFile.addAction(self.saveAction)
        self.menuChoiceFile.addAction(self.saveAsAction)
        self.menuChoiceFile.addAction(self.exitAction)
        
        self.menuChoiceEdit = self.mBar.addMenu('&Edit')
        self.menuChoiceEdit.addAction(self.undoAction)
        self.menuChoiceEdit.addAction(self.redoAction)
        
        self.menuChoiceSettings = self.mBar.addMenu('&Settings')
        self.menuChoiceSettings.addAction(self.configureAction)
        
        self.menuChoiceHelp = self.mBar.addMenu('&Help')
        self.menuChoiceHelp.addAction(self.aboutAction)
        self.menuChoiceHelp.addAction(self.aboutQtAction)
    
    def createToolBar(self):
        ## Create a menu option to enable/disable the toolbar(s). Make the menubar hidable by using a keyboard shortcut as well.
        self.mainToolBar = self.addToolBar('Main')
        self.mainToolBar.addAction(self.newFileAction)
        self.mainToolBar.addAction(self.openAction)
        self.mainToolBar.addAction(self.saveAction)
        self.mainToolBar.addAction(self.saveAsAction)
        self.mainToolBar.addAction(self.fontAction)
        self.mainToolBar.addAction(self.exitAction)
    
    ## Sadly, I can't rely on .setCentralWidget forever. Really, that was used so I didn't have to bother with creating a layout of my own. It
    ## works fine, for now. Once other things are added though, like tabs or little sidebars like the "Code Snippets" things in KDevelop (or the
    ## Konsole thing I use so much), it may not work as expected. Just keep an eye on it, so if the layout mysteriously breaks I can start here.
    ## Speaking of which, the way KDevelop lays out the "tabs" such as the Code Snippets and Konsole ones is a great way to layout character
    ## development/biography pages and plot graphs and the such. Keep it tucked away to the side like that, then when needed, pull it up. Nice.
    def createTextEditor(self):
        self.textEditor = QtGui.QTextEdit()
        self.setCentralWidget(self.textEditor)
        self.textEditor.setStatusTip('Happy Writing!')
        ## This, I don't feel, should be here. Ideally the open file contents are returned and able to be plugged in elsewhere. Like doing this
        ## in a function, or together with openFileDialog() (for when it remembers the last open file). Or something like that. But for now,
        ## it works.
        
        ## Get rid of the try/except here. Do a len(sys.argv), and if it is greater than 1 take sys.argv[1] then and open it. That eliminates the need
        ## for a try/except. Secondly, make this a generic function that checks for command line options and returns True. If True, open the file
        ## it returns as well. Such as "def func():...return True, self.CLIFile"
        try:
            if sys.argv[1]:
                self.CLIFile = open(sys.argv[1], 'U')
                self.CLIFileContents = self.CLIFile.read()
                self.CLIFile.close()
                self.textEditor.setText(self.CLIFileContents)
                self.setWindowTitle('NegaFin PyQt4 - ' + sys.argv[1])
        except:
            pass
    
    def createNewFile(self):
        ## Need to find a way to reset .isModified, so you aren't bothered by it after just saving your file.
        if self.textEditor.document().isModified():
            self.warn = QtGui.QMessageBox.warning(self, 'Save file?', 'Do you wish to save the changes made first?',
                                                QtGui.QMessageBox.Yes, QtGui.QMessageBox.No, QtGui.QMessageBox.Cancel)
            
            ## This is part of what I'm talking about below (function True/False returns). Actually, this isn't the best example because
            ## calling the functions is relevant here...To talk about it here, the main beef I have with it is inserting text into the
            ## editor + saving. I don't want those parts hardcoded into those functions because then those functions can't be used
            ## elsewhere without messing with the QTextEdit widget. That isn't cool, and needs to be fixed!!
            if self.warn == QtGui.QMessageBox.Yes:
                self.saveFileDialog()
                self.textEditor.clear()
                self.setWindowTitle('NegaFin PyQt4')    ## I need to work out dynamically setting the title based on what is open
            elif self.warn == QtGui.QMessageBox.No:     ## (or not open). That way I don't have boatloads of setWindowTitles around,
                self.textEditor.clear()                 ## like I do here.
                self.setWindowTitle('NegaFin PyQt4')
            else:
                pass
        
        else:
            self.textEditor.clear()                 ## It wouldn't be a bad idea to add a warning here as well. Split out the
            self.setWindowTitle('NegaFin PyQt4')    ## warning above into its own function, and add it here. Ideally, the function
                                                    ## should return True/False, and this go based on that instead just calling it.
    
    ## Restructure this to return the file contents, to be plugged into createTextEditor()...Read on.
    ## These functions I don't want to call when I need something done, I want them to be tested for a True/False return value. As in, instead
    ## of testing for something then calling the function to do ALL the work, I want the function to do the testing than the result of that test
    ## used to determine what work needs done where. This way, such things such as inserting text into the editor isn't hardcoded into the
    ## function, because the function may do other things that are useful elsewhere. Keep it this way where appropriate, if I have to jump through
    ## loops just to get a certain function that way then it doesn't need to be implemented that way (for that particular function). Understood?
    def openFileDialog(self):
        self.openFilePath = QtGui.QFileDialog.getOpenFileName(self, 'Open File', self.defaultDir)
        self.openFile = open(self.openFilePath, 'U')    ## Find the best mode to open these files in. Remember what they will be in
        self.openFileContents = self.openFile.read()    ## the future. Plain text most likely won't cut it.
        self.openFile.close()
        self.textEditor.setText(self.openFileContents)
        self.setWindowTitle('NegaFin PyQt4 - ' + self.openFilePath)
    
    ## Here, there is the problem of opening a file on the command line, closing it, then opening another one. The new file you opened and edited
    ## will be saved to the file you opened via the command line instead of the file you newly opened. A bug and a half. This all needs redone
    ## anyway though...0.0.3?
    ## There is also the problem of creating a new file, and pressing save to save it. It should pop up the save dialog, then any further saves
    ## work as expected. However, there is trouble with variables here. openFile doesn't exist unless you open something, using saveFile here
    ## doesn't solve anything either. Plus all these try/except's. Saving and Opening are going to have to be restructed before I can continue.
    
    ## See the comment located with the opening of the sys.argv file about making this into a function that returns True and the CLIFile, or simply
    ## false, to be passed over. Eliminates the try/excepts, makes the code cleaner.
    def saveFile(self):
        try:
            if sys.argv[1]:
                self.CLIFile = open(sys.argv[1], 'w')
                self.saveFileContents = self.textEditor.toHtml()
                self.CLIFile.write(self.saveFileContents)
                self.CLIFile.close()
        except IndexError:
            try:
                self.saveFile = open(self.openFilePath, 'w')
                self.saveFileContents = self.textEditor.toHtml()
                self.saveFile.write(self.saveFileContents)
                self.saveFile.close()
            except AttributeError:
                self.saveAsFileDialog()
    
    ## Something is missing here, or in createTextEditor(), because a newline is not added at the very end...I want a newline to be added! Other
    ## than that, saving works as you would expect. What would be nice (?, didn't finish this line when I typed it...)
    def saveAsFileDialog(self):
        self.saveFilePath = QtGui.QFileDialog.getSaveFileName(self, 'Save File', self.defaultDir)
        print self.saveFilePath
        if self.saveFilePath:
            self.saveFile = open(self.saveFilePath, 'w')
            self.saveFileContents = self.textEditor.toHtml()    ## Set .toHtml so font formatting is saved. Is there a better way?
            self.saveFile.write(self.saveFileContents)
            self.saveFile.close()
            self.setWindowTitle('NegaFin PyQt4 - ' + self.saveFilePath)
    
    def setNewFont(self):
        self.font, self.ignore = QtGui.QFontDialog.getFont()    ## self.ignore is here because getFont returns two objects. The second, a
        self.textEditor.setCurrentFont(self.font)               ## True/False value, isn't needed really. Though I may change this later on.
    
    def about(self):
        QtGui.QMessageBox.about(self, 'About NegaFin PyQt4', '<b>NegaFin PyQt4</b> is an up and coming open source '
                                'novel editor. It will be designed to help you manage your needed information less '
                                'by allowing you to split various things apart so you can better focus on writing.\n'
                                'Less management, improved creativity.\n'
                                'NOTE: This application is far far far from that goal! While it can be used for basic text '
                                'editing, this application by no means promises to do anything more.')
    
    ## Talking about dynamically setting the title, crafting a function that set the title based on various events should work. But to do so
    ## the open/save functions will have to be fixed so they are based on True/False returns before I can do that. Basically, if need be, have
    ## a signal emitted if opening a file returns True which is slotted to the function that sets the window title to the filename of the newly
    ## opened file. Mutatis mutandis for saving or creating a new file.
    def setConnections(self):
        self.connect(self.newFileAction, QtCore.SIGNAL('triggered()'), self.createNewFile)
        self.connect(self.openAction, QtCore.SIGNAL('triggered()'), self.openFileDialog)
        self.connect(self.saveAction, QtCore.SIGNAL('triggered()'), self.saveFile)
        self.connect(self.saveAsAction, QtCore.SIGNAL('triggered()'), self.saveAsFileDialog)
        self.connect(self.exitAction, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))
        
        self.connect(self.undoAction, QtCore.SIGNAL('triggered()'), self.textEditor, QtCore.SLOT('undo()'))
        self.connect(self.redoAction, QtCore.SIGNAL('triggered()'), self.textEditor, QtCore.SLOT('redo()'))
        
        self.connect(self.fontAction, QtCore.SIGNAL('triggered()'), self.setNewFont)
        
        self.connect(self.aboutAction, QtCore.SIGNAL('triggered()'), self.about)
        self.connect(self.aboutQtAction, QtCore.SIGNAL('triggered()'), QtGui.qApp, QtCore.SLOT('aboutQt()'))

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    negaFin = NegaFinSub()
    negaFin.show()
    sys.exit(app.exec_())